package kerneltracing

import (
	"bytes"
	"encoding/json"
	"errors"
	"flare-admin/db/dao"
	"flare-admin/db/sqlc/reversesh"
	"flare-admin/ecode"
	"flare-admin/service/dto/micro"
	reverseshdto "flare-admin/service/dto/reversesh"
	"flare-admin/service/external/hclient"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgconn"
	"github.com/jackc/pgx/v5/pgtype"
	"github.com/sirupsen/logrus"
	"io"
	"net/http"
	"time"
)

type Service struct {
	dao *dao.DBTXQuery
}

func New(dao *dao.DBTXQuery) *Service {
	return &Service{
		dao: dao,
	}
}

func (s *Service) GetAllReverseSgConfig(ctx *gin.Context) ([]micro.ReverseShConfigDTO, error) {
	configs, err := s.dao.ReverseSh.SelectAllConfig(ctx)
	if err != nil {
		logrus.Errorf("[service] - [GetAllPattern] - failed to get all pattern: %v", err)
	}
	return micro.ReverseShConfigDao2DTOs(configs), nil
}

// AddReverseShConfig 返回false代表违反唯一键约束
func (s *Service) AddReverseShConfig(ctx *gin.Context, ipAddr string) (bool, error) {
	// ipAddr的校验已经在前面做过了
	//res, err := s.dao.ReverseSh.SelectConfigByNodeIP(ctx, ipAddr)
	//if err != nil && !errors.Is(err, pgx.ErrNoRows) {
	//	logrus.Errorf("[service] - [AddReverseConfig] - failed to get config by ip: %v", err)
	//	return false, err
	//}
	//if res.NodeIp == ipAddr {
	//	logrus.Infof("[service] - [AddReverseConfig] - ip %s already exists", ipAddr)
	//	return false, nil
	//}
	// 插入数据，数据库中有唯一键约束
	if _, err := s.dao.ReverseSh.InsertConfig(ctx, reversesh.InsertConfigParams{NodeIp: ipAddr, PluginEnabled: false}); err != nil {

		var pgerr *pgconn.PgError
		if errors.As(err, &pgerr) && pgerr.Code == "23505" {
			// 23505: unique_violation
			logrus.Infof("[service] - [AddReverseConfig] - ip %s already exists", ipAddr)
			return false, nil
		}
		logrus.Errorf("[service] - [AddReverseConfig] - failed to insert config: %v, err type = %T", err, err)
		return false, err
	} else {
		return true, nil
	}
}

func (s *Service) UpdateReverseShConfig(ctx *gin.Context, ipAddr string, pluginEnabled bool) (bool, error) {
	// ipAddr的校验已经在前面做过了
	res, err := s.dao.ReverseSh.UpdateConfig(ctx, reversesh.UpdateConfigParams{NodeIp: ipAddr, PluginEnabled: pluginEnabled})
	if err != nil {
		logrus.Errorf("[service] - [UpdateReverseShConfig] - failed to update config by ip: %v", err)
		return false, err
	}
	return len(res) == 1, nil
}

// EnableReverseShPluginInNode 向node发送请求，开启插件，同时更新数据库
func (s *Service) EnableReverseShPluginInNode(ctx *gin.Context, nodeIP string, enable bool) error {
	client := hclient.NewHttpClient()
	bodyData := struct {
		Plugin string `json:"plugin"`
	}{
		Plugin: "reverseSh",
	}
	bodyDataBytes, _ := json.Marshal(bodyData)

	err := s.dao.ExecTx(ctx, pgx.RepeatableRead, func(queries *dao.DBTXQuery) error {
		var req *http.Request
		var err error
		if enable {
			req, err = http.NewRequest("POST",
				fmt.Sprintf("http://%s:8080/ebpf/plugin/enable", nodeIP),
				bytes.NewReader(bodyDataBytes))
		} else {
			req, err = http.NewRequest("POST",
				fmt.Sprintf("http://%s:8080/ebpf/plugin/disable", nodeIP),
				bytes.NewReader(bodyDataBytes))
		}

		if err != nil {
			logrus.Errorf("[Svc] - [EnableReverseShPluginInNode] - failed to create request: %v", err)
			return err
		}

		resp, err := client.Do(req)
		if err != nil {
			logrus.Errorf("[Svc] - [EnableReverseShPluginInNode] - failed to send request: %v", err)
			return ecode.ErrEnablePlugin
		}
		respBodyBytes, err := io.ReadAll(resp.Body)
		if err != nil {
			logrus.Errorf("[Svc] - [EnableReverseShPluginInNode] - failed to read response body: %v", err)
			return ecode.ErrEnablePlugin
		}

		if string(respBodyBytes) == "true" {
			// 风险： 可能有数据不一致的问题
			res, err := queries.ReverseSh.UpdateConfig(ctx, reversesh.UpdateConfigParams{NodeIp: nodeIP, PluginEnabled: enable})
			if err != nil || len(res) != 1 {
				logrus.Errorf("[EnableReverseSh] - [UpdateReverseShConfig] - 数据库更新失败，但是plugin打开了，校验ip = %s的一致性", nodeIP)
				return ecode.ErrEnablePlugin
			}
		} else {
			logrus.Errorf("[Svc] - [EnableReverseShPluginInNode] - failed to enable plugin in node: %v", string(respBodyBytes))
			return ecode.ErrEnablePlugin
		}
		return nil
	})

	return err

}

func (s *Service) UploadReverseShRecord(ctx *gin.Context, ctids []uint64, message string, kernelFuncName string, nodeName string, eventTime string) (string, error) {
	if len(ctids) == 0 {
		return "", nil
	}
	t, err := time.Parse(eventTime, time.RFC3339)
	if err != nil {
		logrus.Errorf("[UploadReverseShRecord] - 时间格式解析失败: %v", err)
		return "", ecode.ErrRequestParam
	}
	pgt := pgtype.Timestamptz{}
	if err = pgt.Scan(t); err != nil {
		logrus.Errorf("[UploadReverseShRecord] - 时间转换pg时间失败: %v", err)
		return "", ecode.ErrRequestParam
	}

	for _, ctid := range ctids {
		if _, err := s.dao.ReverseSh.InsertReverseShellRecord(ctx, reversesh.InsertReverseShellRecordParams{
			CybertwinID: int32(ctid),
			Syscall:     kernelFuncName,
			NodeName:    nodeName,
			EventTime:   pgt,
		}); err != nil {
			logrus.Errorf("[UploadReverseShRecord] - 插入反向Shell记录失败: %v", err)
			return "", err
		}
	}
	return "success", nil
}

func (s *Service) GetAllReverseShAuditResults(ctx *gin.Context) ([]reverseshdto.AuditRecordDTO, error) {
	res, err := s.dao.ReverseSh.SelectAllReverseShAuditResults(ctx)
	if err != nil {
		logrus.Errorf("[GetAllReverseShAuditResults] - failed to get all reverse shell audit results: %v", err)
		return nil, ecode.ErrDBExecution
	}

	return reverseshdto.ReverseShAuditResultDao2DTOs(res), nil
}
